package com.lhs.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lhs.config.redis.RedisKeyConstants;
import com.lhs.config.redis.ZiDeRedisUtil;
import com.lhs.entity.blog.blog_post;
import com.lhs.mapper.blog_post_0Mapper;
import com.lhs.service.BlogViewService;
import com.lhs.service.HotBlogService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class BlogViewServiceImpl extends ServiceImpl<blog_post_0Mapper, blog_post> implements BlogViewService {


    @Autowired
    private ZiDeRedisUtil ziDeRedisUtil;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private blog_post_0Mapper blogPostMapper;
    @Autowired
    private HotBlogService hotBlogService;

    /**
     * 增加浏览计数
     * 采用异步方式处理，优先保证响应速度[4](@ref)
     */
    @Override
    @Transactional // 声明式事务
    public void incrementViewCount(Long postId, Long userId) {
        if (postId == null) {
            return;
        }

        // 1. 防刷检查：同一用户30秒内重复阅读不计入
        if (userId != null) {
            String rateLimitKey = String.format(RedisKeyConstants.USER_VIEW_RATE_LIMIT, userId, postId);
            Boolean canView = ziDeRedisUtil.setIfAbsent(rateLimitKey, "1", 30, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(canView)) {
                return;
            }
        }

        // 2. 使用HyperLogLog进行用户去重浏览统计（如果需要用户级别的去重）
        if (userId != null) {
            String viewedUsersKey = String.format(RedisKeyConstants.BLOG_VIEWED_USERS, postId);
            ziDeRedisUtil.pfAdd(viewedUsersKey, userId.toString());
        }

        // 3. 增加浏览计数
        String viewCountKey = String.format(RedisKeyConstants.BLOG_VIEW_COUNT, postId);
        ziDeRedisUtil.increment(viewCountKey, 1L);

        // 4. 异步更新数据库
        asyncUpdateViewToDatabase(postId);

        // 5. 判断文章是否为热点，更新热点文章列表
        hotBlogService.checkAndUpdateHotBlog(postId);
    }

    /**
     * 异步更新浏览数到数据库[1](@ref)
     */
    @Async
    public void asyncUpdateViewToDatabase(Long postId) {
        try {
            // 获取当前Redis中的浏览数
            String viewCountKey = String.format(RedisKeyConstants.BLOG_VIEW_COUNT, postId);
            Long viewCount = ziDeRedisUtil.getCacheObject(viewCountKey);

            if (viewCount != null) {
                // 使用Redisson锁保证数据库更新安全
                String lockKey = "lock:db:blog:view:" + postId;
                RLock lock = redissonClient.getFairLock(lockKey);

                try {
                    if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                        blogPostMapper.updateViewCount(postId, viewCount);
                        // 更新成功后，可以清空或减少Redis中的计数，也可以保留作为缓存
                    }
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } catch (Exception e) {
            log.error("异步更新浏览数数据库失败，postId: {}", postId, e);
            // 可以将失败任务放入重试队列
        }
    }

    /**
     * 获取文章浏览数
     */
    @Override
    public Long getBlogViewCount(Long postId) {
        String viewCountKey = String.format(RedisKeyConstants.BLOG_VIEW_COUNT, postId);
        Long viewCount = ziDeRedisUtil.getCacheObject(viewCountKey);

        if (viewCount == null) {
            // 缓存未命中，从数据库加载
            viewCount = blogPostMapper.selectViewCountByPostId(postId);
            if (viewCount != null) {
                ziDeRedisUtil.setCacheObject(viewCountKey, viewCount, 1, TimeUnit.DAYS);
            }
        }

        return viewCount;
    }


}
